package org.zoomdev.zoom.common.utils;

import org.apache.commons.lang3.ArrayUtils;
import org.zoomdev.zoom.common.Destroyable;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.filter.Filter;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public final class Classes {

    /**
     * 空
     */
    public static final Object[] EMPTY = new Object[0];

    public static Filter<Method> NOT_PRIVATE_FINAL = new Filter<Method>() {
        @Override
        public boolean accept(Method value) {
            int modifiers = value.getModifiers();
            return !Modifier.isFinal(modifiers) && !Modifier.isPrivate(modifiers);
        }
    };

    public static Class<?> getClass(Type type) {
        assert (type != null);
        if (type instanceof Class) {
            return (Class<?>) type;
        }
        if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        }
        throw new ZoomException("不能获取class " + type);
    }

    public static Collection newCollection(Class<?> type) {
        return Creators.newCollection(type);
    }

    /**
     * 直接初始化一个对象
     *
     * @param type
     * @return
     */
    public static Object newInstance(Class<?> type) {
        return Creators.newInstance(type);
    }


    public static ZoomException makeThrow(Throwable e) {
        return Creators.makeThrow(e);
    }

    /**
     * 获取一个类的所有泛型信息
     *
     * @param clazz
     * @return
     */
    public static Type[] getAllParameterizedTypes(final Class<?> clazz) {
        if (clazz == null || "java.lang.Object".equals(clazz.getName()))
            return null;
        // 看看父类
        Type superclass = clazz.getGenericSuperclass();
        if (null != superclass && superclass instanceof ParameterizedType)
            return ((ParameterizedType) superclass).getActualTypeArguments();

        // 看看接口
        Type[] interfaces = clazz.getGenericInterfaces();
        for (Type inf : interfaces) {
            if (inf instanceof ParameterizedType) {
                return ((ParameterizedType) inf).getActualTypeArguments();
            }
        }
        return getAllParameterizedTypes(clazz.getSuperclass());
    }

    public static Type[] getParameterizedTypes(final Type clazz) {
        if (clazz == null)
            return null;

        if (clazz instanceof ParameterizedType) {
            return ((ParameterizedType) clazz).getActualTypeArguments();
        }

        return null;
    }

    /**
     * dest 是否 是 src的包装类
     *
     * @param src
     * @param dest
     * @return
     */
    public static boolean isWapClass(Class<?> src, Class<?> dest) {
        try {
            return dest.getField("TYPE").get(null) == src;
        } catch (Exception e) {
            return false;
        }
    }

    public static Field getAccessableField(Class<?> clazz, String name) {
        Field field = getField(clazz, name);
        field.setAccessible(true);
        return field;
    }

    public static Field getField(Class<?> clazz, String name) {

        Field result;
        try {
            result = clazz.getField(name);
            return result;
        } catch (NoSuchFieldException e) {

        }
        do {
            try {
                result = clazz.getDeclaredField(name);
                return result;
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
                if (clazz == Object.class) {
                    return null;
                    //throw new ZoomException("Cannot find field " + name + " in " + clazz.getName());
                }
            }

        } while (true);
    }

    /**
     * @param clazz
     */
    public static Field fetchField(Class<?> clazz, String name) {
        Field field = getField(clazz, name);
        if (field == null) {
            throw new ZoomException("Cannot find filed " + name + " in class " + clazz);
        }
        return field;
    }

    /**
     * 获取一个类的所有Field
     *
     * @param clazz 对象类
     * @return 所有获取到的Field
     */
    public static List<Field> getFields(Class<?> clazz) {
        assert (clazz != null && !clazz.isInterface());
        List<Field> result = new ArrayList<>();
        Class<?> tmp = clazz;
        do {
            getFields(result, tmp);
            tmp = tmp.getSuperclass();
            if (tmp == Object.class) {
                break;
            }
        } while (true);
        return result;
    }

    /**
     * java包的class，例如int等类型 、 java. javax.
     *
     * @param type
     * @return
     */
    public static boolean isJavaClass(Class<?> type) {
        if (type.isPrimitive()) {
            return true;
        } else return type.getName().startsWith("java");
    }

    /**
     * @return 当前对象是否为枚举
     */
    public static boolean isEnum(Class<?> type) {
        return type.isEnum();
    }

    public static boolean isSimple(Class<?> type) {
        return isString(type) || isBoolean(type) || isChar(type) || isNumber(type) || isDateTime(type) || isEnum(type);
    }

    public static boolean isDateTime(Class<?> type) {
        return Calendar.class.isAssignableFrom(type)
                || Date.class.isAssignableFrom(type)
                || java.sql.Date.class.isAssignableFrom(type)
                || java.sql.Time.class.isAssignableFrom(type);
    }

    public static boolean isString(Class<?> src) {
        return CharSequence.class.isAssignableFrom(src);
    }

    public static boolean isBoolean(Class<?> src) {
        return is(src, Boolean.class) || is(src, boolean.class);
    }

    public static boolean isChar(Class<?> src) {
        return is(src, Character.class) || is(src, char.class);
    }

    public static boolean isNumber(Class<?> src) {
        return Number.class.isAssignableFrom(src)
                || src == int.class
                || src == double.class
                || src == float.class
                || src == byte.class
                || src == short.class
                || src == long.class;
    }

    public static boolean isFloat(Class<?> src) {
        return Double.class.isAssignableFrom(src) || Float.class.isAssignableFrom(src)
                || BigDecimal.class.isAssignableFrom(src)
                || src == double.class
                || src == float.class;
    }

    public static boolean isInteger(Class<?> src) {
        return Integer.class == src || Long.class == src || Short.class == src || Byte.class == src
                || src == int.class || src == long.class
                || src == byte.class || src == short.class;
    }

    public static boolean is(Class<?> src, Class<?> desc) {
        return null != desc && src == desc;
    }

    /**
     * 获取所有的public方法
     *
     * @param type
     * @return
     */
    public static List<Method> findPublicMethods(Class<?> type) {
        if (type == Object.class) {
            return Collections.emptyList();
        }
        Method[] methods = type.getMethods();
        List<Method> list = new ArrayList<>(methods.length);
        for (Method method : methods) {
            if(method.isBridge() || method.isSynthetic()){
                continue;
            }

            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }
            if (method.getDeclaringClass() == Object.class) {
                continue;
            }
            list.add(method);
        }
        return list;
    }



    public static List<Method> findMethods(Class<?> type, Filter<Method> methodFilter) {
        List<Method> list = new ArrayList<>();
        findMethods(type, methodFilter, list);
        return list;
    }

    /**
     * 判断clazz是否实现了接口interfaceClass
     *
     * @param clazz
     * @param interfaceClass
     * @return
     */
    public static boolean hasInterface(Class<?> clazz, Class<?> interfaceClass) {
        Class<?>[] interfacesArray = clazz.getInterfaces();
        for (Class<?> i : interfacesArray) {
            if (i == interfaceClass)
                return true;
        }
        return false;
    }

    /**
     * 比较两个class是否是等价的，与==不同，这个方法可以判断包装类，如 isEqual(Integer.class,int.class)==true
     *
     * @param srcClass
     * @param destClass
     * @return
     */
    public static boolean isEqual(Class<?> srcClass, Class<?> destClass) {
        return srcClass == destClass || isWapClass(srcClass, destClass) || isWapClass(destClass, srcClass);
    }

    /**
     * 设置静态字段的值
     *
     * @param clazz
     * @param name  字段名称
     * @param value
     * @throws Exception
     */
    public static void set(Class<?> clazz, String name, Object value) throws Exception {
        Field field = fetchField(clazz, name);
        field.setAccessible(true);
        field.set(null, value);
    }

    /**
     * 设置某个字段的值
     *
     * @param clazz
     * @param instance
     * @param name
     * @param value
     * @throws Exception
     */
    public static void set(Class<?> clazz, Object instance, String name, Object value) {
        try {
            Field field = fetchField(clazz, name);
            field.setAccessible(true);
            field.set(instance, value);
        } catch (Exception e) {
            throw new ZoomException(e);
        }
    }

    /**
     * 获取错误原因，到非InvocationTargetException为止
     *
     * @param e
     * @return
     */
    public static Throwable getCause(Throwable e) {
        if (e instanceof InvocationTargetException) {
            return getCause(((InvocationTargetException) e).getTargetException());
        }
        return e;
    }

    public static void destroy(Map<?, ?> map) {
        for (Entry<?, ?> entry : map.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof Destroyable) {
                ((Destroyable) value).destroy();
            }
        }
        map.clear();
    }

    public static void destroy(Collection<?> collection) {
        for (Object value : collection) {
            if (value instanceof Destroyable) {
                ((Destroyable) value).destroy();
            }
        }
        collection.clear();
    }

    public static <T> void destroy(T[] array) {
        for (Object value : array) {
            if (value instanceof Destroyable) {
                ((Destroyable) value).destroy();
            }
        }

        Arrays.fill(array, null);
    }

    public static void destroy(Object target) {
        if (target instanceof Destroyable) {
            ((Destroyable) target).destroy();
        }
    }

    /**
     * 直接调用Class.forName,避免抛出异常
     *
     * @param className
     * @return
     */
    public static Class<?> forName(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new ZoomException(e);
        }
    }

    /**
     * 增强版堆栈跟踪格式化方法
     *
     * @param throwable    要格式化的异常对象
     * @param includeCause 是否包含原因异常
     * @param maxDepth     最大堆栈深度（0表示无限制）
     * @return 格式化的堆栈跟踪字符串
     */
    public static String formatStackTrace(Throwable throwable, boolean includeCause, int maxDepth) {
        if (throwable == null) {
            return "Null exception provided";
        }

        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);

        // 格式化主异常
        formatSingleException(throwable, printWriter, maxDepth);

        // 递归处理原因异常
        if (includeCause) {
            Throwable cause = throwable.getCause();
            while (cause != null) {
                printWriter.println("\nCaused by:");
                formatSingleException(cause, printWriter, maxDepth);
                cause = cause.getCause();
            }
        }

        // 添加抑制异常
        if (throwable.getSuppressed().length > 0) {
            printWriter.println("\nSuppressed exceptions:");
            for (Throwable suppressed : throwable.getSuppressed()) {
                printWriter.println("--- Suppressed exception ---");
                formatSingleException(suppressed, printWriter, maxDepth);
            }
        }

        printWriter.flush();
        return stringWriter.toString();
    }

    public static String formatStackTrace(Throwable result) {
        assert (result != null);
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
        result.printStackTrace(printWriter);
        return stringWriter.toString();
    }

    public static Object newInstance(Constructor constructor, Object... args) {
        return Creators.newInstance(constructor,args);
    }

    /**
     * 根据args查找构造函数
     *
     * @param args
     * @return
     */
    public static Constructor<?> findConstructor(Class<?> type, Class<?>... args) {
        Constructor<?>[] constructors = type.getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (getParameterCount(constructor) == args.length) {
                if (Types.matches(constructor.getGenericExceptionTypes(), args)) {
                    return constructor;
                }
            }
        }
        return null;
    }

    /**
     * 寻找无参数构造函数
     *
     * @param type
     * @return
     */
    public static Constructor<?> findNoneParameterConstructor(Class<?> type) {
        assert (type != null);
        Constructor<?>[] constructors = type.getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (getParameterCount(constructor) == 0) {
                return constructor;
            }
        }
        return null;
    }

    public static boolean isInterface(Type type) {
        return (type instanceof Class) && ((Class<?>) type).isInterface();
    }

    public static boolean isAnnotationPresent(Type type, Class<? extends Annotation> anno) {
        return (type instanceof Class) && ((Class<?>) type).isAnnotationPresent(anno);
    }

    public static int getParameterCount(Constructor<?> constructor) {
        return constructor.getParameterTypes().length;
    }

    public static int getParameterCount(Method method) {
        return method.getParameterTypes().length;
    }

    public static String[] getClassPaths() {
        String str = System.getProperty("java.class.path");
        return str.split(File.pathSeparator);
    }

    /**
     * 提供泛型List type的构造
     *
     * @param elementClass
     * @return
     */
    public static ParameterizedType listType(Class<?> elementClass) {
        return Creators.listType(elementClass);
    }

    /**
     * 是否抽象类
     *
     * @param type
     * @return
     */
    public static boolean isAbstract(Class<?> type) {
        assert (type != null);
        return Modifier.isAbstract(type.getModifiers());
    }

    public static boolean isClass(Type type) {
        return type instanceof Class;
    }

    public static Method getGetter(Class<?> type, String fieldName) {
        String name = NameCaseConverter.capitalizeFirstLetter(fieldName);
        Method method = _getGetter(type, name);
        if (method == null) {
            if (fieldName.length() > 2 && Character.isUpperCase(fieldName.charAt(1))) {
                method = _getGetter(type, fieldName);
            }
        }
        return method;
    }

    public static Method getSetter(Class<?> type, String fieldName, Class<?> paramClass) {
        try {
            String name = NameCaseConverter.capitalizeFirstLetter(fieldName);
            return type.getDeclaredMethod("set" + name, paramClass);
        } catch (NoSuchMethodException e) {
            if (fieldName.length() > 2 && Character.isUpperCase(fieldName.charAt(1))) {
                try {
                    return type.getDeclaredMethod("set" + fieldName, paramClass);
                } catch (NoSuchMethodException ex) {

                }
            }
            return null;
        }
    }

    public static boolean isAssignableFrom(Class<?> src, Type type) {
        if (type instanceof Class) {
            return src.isAssignableFrom((Class<?>) type);
        }
        if (type instanceof ParameterizedType) {
            return isAssignableFrom(src, ((ParameterizedType) type).getRawType());
        }
        return false;
    }

    /**
     * obj是否是type的子类
     *
     * @param obj
     * @param type
     */
    public static boolean instanceOf(Object obj, Class<?> type) {
        if (obj == null || type == null) {
            return false;
        }

        if (type.isInterface()) {
            return ArrayUtils.contains(obj.getClass().getInterfaces(), type);
        }

        Class<?> clazz = obj.getClass();
        if (clazz == type) {
            return true;
        }
        while (clazz != Object.class) {
            clazz = clazz.getSuperclass();
            if (clazz == type) {
                return true;
            }
        }

        return false;
    }

    public static void set(Field field, Object obj, Object value) {
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            throw new ZoomException(e);
        }
    }

    public static Object get(Field srcField, Object src) {
        try {
            return srcField.get(src);
        } catch (IllegalAccessException e) {
            throw new ZoomException(e);
        }
    }

    public static String getMethodDesc(Class targetClass,Method method){

        Parameter[] parameters = method.getParameters();
        boolean first = true;
        StringBuilder sb = new StringBuilder();
        sb.append(targetClass.getSimpleName()).append("#").append(method.getName()).append("(");
        for (Parameter parameter : parameters) {
            if (first) {
                first = false;
            } else {
                sb.append(",");
            }
            sb.append(parameter.getType().getSimpleName());
        }
        sb.append(")");

        return sb.toString();
    }

    /**
     * 方法简单描述
     *
     * @param method
     * @return
     */
    public static String getMethodDesc(Method method) {
        return getMethodDesc(method.getDeclaringClass(),method);
    }

    public static Constructor getConstructor(Class type, Class... paramTypes) {
        try {
            return type.getConstructor(paramTypes);
        } catch (NoSuchMethodException e) {
            throw new ZoomException(e);
        }
    }

    /**
     * 格式化单个异常（不含原因）
     */
    private static void formatSingleException(Throwable throwable, PrintWriter printWriter, int maxDepth) {
        // 输出异常基本信息
        printWriter.println(throwable.getClass().getName() + ": " + throwable.getMessage());

        // 输出堆栈跟踪
        StackTraceElement[] stackTrace = throwable.getStackTrace();
        int depth = maxDepth > 0 ? Math.min(maxDepth, stackTrace.length) : stackTrace.length;

        for (int i = 0; i < depth; i++) {
            printWriter.println("\tat " + stackTrace[i]);
        }

        // 如果截断了堆栈，显示提示
        if (maxDepth > 0 && depth < stackTrace.length) {
            int remaining = stackTrace.length - depth;
            printWriter.println("\t... " + remaining + " more");
        }
    }

    // 获取所有
    private static void getFields(List<Field> result, Class<?> clazz) {
        assert (result != null && clazz != null);

        if (clazz.getName().startsWith("java")) {
            return;
        }
        try {
            Field[] list = clazz.getDeclaredFields();
            for (Field field : list) {
                if ((field.getModifiers() & Modifier.STATIC) > 0) {
                    continue;
                }
                if (field.getName().equals("class")) {
                    continue;
                }
                field.setAccessible(true);
                result.add(field);
            }
        } catch (Throwable e) {
            throw new ZoomException(String.format("在获取%s的field的时候发生异常", clazz), e);
        }

    }


    private static void findMethods(Class<?> type, Filter<Method> methodFilter, List<Method> list) {
        if (type == Object.class || type.getName().startsWith("java")) {
            return;
        }
        Method[] methods = type.getDeclaredMethods();
        for (Method method : methods) {
            if(method.isSynthetic() || method.isBridge()){
                continue;
            }
            if (!methodFilter.accept(method)) {
                continue;
            }
            if(contains(list,method)){
                continue;
            }
            list.add(method);
        }
        findMethods(type.getSuperclass(), methodFilter, list);
    }

    private static Method _getGetter(Class<?> type, String name) {
        try {
            return type.getMethod("get" + name);
        } catch (NoSuchMethodException e) {
            try {
                return type.getMethod("is" + name);
            } catch (NoSuchMethodException ex) {
                return null;
            }
        }
    }



    private static boolean contains(List<Method> list, Method method) {
        for (Method mth : list) {
            if (mth.getName().equals(method.getName())
                    && Arrays.equals(mth.getParameterTypes(), method.getParameterTypes())) {
                return true;
            }
        }
        return false;
    }


    public static boolean isJava8(){
        String version = System.getProperty("java.version");
        return version.startsWith("1.8");
    }
}
